package com.tencent.sr.iris.activity.service.event.task;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tencent.sr.iris.activity.common.enums.TaskBatchDateIsParticipatedEnum;
import com.tencent.sr.iris.activity.common.enums.UserTaskStatusEnum;
import com.tencent.sr.iris.activity.dependency.dto.data.CommissionInfoDTO;
import com.tencent.sr.iris.activity.domain.event.dto.ActivityDTO;
import com.tencent.sr.iris.activity.domain.event.dto.ActivityTaskDTO;
import com.tencent.sr.iris.activity.domain.event.dto.InviteLimitDTO;
import com.tencent.sr.iris.activity.interfaces.enums.ActivityEventTypeEnum;
import com.tencent.sr.iris.activity.interfaces.enums.OrderStatusEnum;
import com.tencent.sr.iris.activity.interfaces.event.dto.ActivityEventDTO;
import com.tencent.sr.iris.activity.interfaces.message.DistributionOrderEvent;
import com.tencent.sr.iris.activity.repository.entity.TIrisActivityAwardRecordDO;
import com.tencent.sr.iris.activity.repository.entity.TIrisUserTaskRecordDO;
import com.tencent.sr.iris.activity.service.activity.ActivityCommonService;
import com.tencent.sr.iris.activity.service.activity.IrisUserTaskRecordService;
import com.tencent.sr.iris.activity.service.config.ActivityConfig;
import com.tencent.sr.iris.activity.service.config.ResponseMockConfig;
import com.tencent.sr.iris.activity.service.constant.ActivityRedisKey;
import com.tencent.sr.iris.activity.service.event.util.ActivityCommonUtilService;
import com.tencent.sr.iris.activity.service.event.vo.BaseUserTaskDataVO;
import com.tencent.sr.iris.activity.service.event.vo.TaskExecutorExtendInfo;
import com.tencent.sr.iris.activity.service.util.IdCreateorUtil;
import com.tencent.sr.iris.activity.service.util.LocalDateUtil;
import com.tencent.sr.rmall.common.exception.retail.TRetailBizException;
import com.tencent.sr.rmall.common.exception.retail.TRetailErrorException;
import com.tencent.sr.rmall.springbootstarter.utils.JsonUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class IrisInviteBecomeTraineeTaskExecutor extends AbstractIrisTaskExecutor {

    @Resource
    private ActivityCommonService activityCommonService;

    @Resource
    private IdCreateorUtil idCreateorUtil;

    @Resource
    private IrisUserTaskRecordService userTaskRecordService;

    @Resource
    private ActivityConfig activityConfig;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private ResponseMockConfig responseMockConfig;

    @Resource
    private ActivityCommonUtilService activityCommonUtilService;

    /**
     * 分布式锁最大执行时间30秒
     */
    private static final int MAX_LOCK_TIME = 30;

    @Resource
    private IrisInviteNewTaskExecutor irisInviteNewTaskExecutor;

    /**
     * 重写
     * 邀请有礼活动,以成为见习会员时间做任务时间准入的校验
     *
     * @param eventDTO
     * @param act
     * @param task
     */
    @Override
    void before(ActivityEventDTO eventDTO, ActivityDTO act, ActivityTaskDTO task) {
        //查询成为见习时间
        Long becomeTraineeTime = activityCommonService.queryTraineeTime(eventDTO.getUid());
        if (becomeTraineeTime < task.getStartTime().getTime() || becomeTraineeTime > task.getEndTime().getTime()) {
            log.info("任务处理：用户成为见习会员时间不在任务时间范围内,不处理，eventIdempotent:{},traineeTime:{},taskId:{},taskTime:{}-{}",
                    eventDTO.getIdempotent(), becomeTraineeTime, task.getBizId(), task.getStartTime(),
                    task.getEndTime());
            throw new TRetailBizException("见习会员时间不在任务时间范围内，不处理");
        }
        if (becomeTraineeTime < activityCommonService.queryInviteChangeTime(eventDTO.getUid())) {
            log.info("任务处理：用户成为见习会员时间在任务切换时间前,不处理，eventIdempotent:{},traineeTime:{},taskId:{},taskTime:{}-{}",
                    eventDTO.getIdempotent(), becomeTraineeTime, task.getBizId(), task.getStartTime(),
                    task.getEndTime());
            throw new TRetailBizException("见习会员时间在任务切换时间前，不处理");
        }
    }

    @SneakyThrows
    public void processOld(ActivityEventDTO msg, ActivityDTO activity, ActivityTaskDTO task,
            List<String> eventHandingInfoList){
        RLock lock = null;
        try {
            //解析任务执行条件门槛值,区分测试
            addParameterForTask(task, msg);
            log.info("任务门槛值,commissionLimit={},shareTimeLimit={},msgId:{}", task.getCommissionLimit(),
                    task.getShareTimeLimit(), msg.getIdempotent());
            //查询任务完成进度
            TIrisUserTaskRecordDO userTaskRecordDO = queryUserTaskRecord(msg.getUid(), activity.getBizId(),
                    task.getBizId(), null);
            //已完成的不再更新进度
            if (Objects.nonNull(userTaskRecordDO)) {
                if (UserTaskStatusEnum.FINISHED.getCode() == userTaskRecordDO.getUserTaskStatus()) {
                    //已完成任务,不再更新进度
                    eventHandingInfoList.add("任务已完成,不再更新进度,userTaskRecordId=" + userTaskRecordDO.getBizId());
                    log.info("任务已完成,不再更新进度,userTaskRecordId={},msgId:{}", userTaskRecordDO.getBizId()
                            , msg.getIdempotent());
                    return;
                }
                if (UserTaskStatusEnum.UNFINISHED.getCode() == userTaskRecordDO.getUserTaskStatus()) {
                    //未完成任务,直接跳过
                    log.info("任务未完成,不再更新该任务进度,userTaskRecordId={}", userTaskRecordDO.getBizId());
                    eventHandingInfoList.add("任务未完成,不再更新该任务进度,userTaskRecordId=" + userTaskRecordDO.getBizId());
                    return;
                }
            } else {
                String lockKey = ActivityRedisKey.getInviteTaskLockKey(msg.getPid());
                lock = redissonClient.getLock(lockKey);
                if (!lock.tryLock(MAX_LOCK_TIME, TimeUnit.SECONDS)) {
                    eventHandingInfoList.add("任务处理：获取分布式锁失败，当前上级邀请任务正在处理");
                    log.info("任务处理：获取分布式锁失败，当前上级邀请任务正在处理,pid:{},uid:{},msgId:{}",
                            msg.getPid(), msg.getUid(), msg.getIdempotent());
                    //跑个异常重试
                    throw new TRetailErrorException("获取分布式锁失败");
                }
                //查询成为见习时间
                Long becomeTraineeTime = activityCommonService.queryTraineeTime(msg.getUid());
                //查询是否已达邀请上限
                InviteLimitDTO inviteLimitDTO = queryInviteLimit(msg.getPid(), becomeTraineeTime);
                log.info("查询上限结果，inviteLimitDTO:{},uid={},pid={},msgId:{}", JSON.toJSONString(inviteLimitDTO)
                        , msg.getUid(), msg.getPid(), msg.getIdempotent());
                if (!inviteLimitDTO.isSatisfy()) {
                    log.info("不满足邀请上限规则,uid={},pid={},errorMsg:{}, msg={}", msg.getUid(), msg.getPid(),
                            inviteLimitDTO.getDesc(),
                            JSONObject.toJSONString(msg));
                    eventHandingInfoList.add("不满足邀请上限规则,inviteLimitDTO:" +
                            JSON.toJSONString(inviteLimitDTO) + ",errorMsg=" + inviteLimitDTO.getDesc());
                }
                //新增用户活动任务完成进度
                TIrisUserTaskRecordDO irisUserTaskRecordDO = buildUserTaskRecordDO(
                        activity, task, msg, becomeTraineeTime, inviteLimitDTO);
                userTaskRecordDO = irisUserTaskRecordDO;
            }
            ///查询佣金
            CommissionInfoDTO commissionInfoDTO = getCommissionInfo(msg.getUid(),
                    userTaskRecordDO.getCurrentTaskStartTime(), userTaskRecordDO.getCurrentTaskEndTime());
            //更新进度参数
            userTaskRecordDO.setEstimateCommission(commissionInfoDTO.getEstimateCommission());

            //任务周期结束时间 + 60天以后,计算是否达到门槛：
            //新增判断：T+60以后(不包含T+60天)，【分佣成功】+【订单交易完成且分佣未取消】是否达门槛（T为下单时间）
            int plusDays = activityConfig.getCommissionPlusDays(userTaskRecordDO.getUid());
            LocalDate taskEndDatePlus60 = userTaskRecordDO.getCurrentTaskEndTime().toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDate().plusDays(plusDays);

            Integer commission =
                    Objects.isNull(commissionInfoDTO.getCommission()) ? 0 : commissionInfoDTO.getCommission();
            LocalDate currentLocalDate = LocalDate.now();
            if (currentLocalDate.isAfter(taskEndDatePlus60)) {
                Integer waitCommissionAmt = Objects.isNull(commissionInfoDTO.getWaitCommissionAmt()) ? 0
                        : commissionInfoDTO.getWaitCommissionAmt();
                commission = commission + waitCommissionAmt;
            }
            userTaskRecordDO.setCommission(commission);
            userTaskRecordDO.setGmv(commissionInfoDTO.getGmv());

            //佣金明细
            String extInfo = activityCommonUtilService.buildTaskRecordExtInfo(userTaskRecordDO.getExtInfo(), commissionInfoDTO);
            userTaskRecordDO.setExtInfo(extInfo);

            userTaskRecordDO.setShareCount(commissionInfoDTO.getShareUvTo29());

            //填充任务完成状态
            fillTaskRecordStatus(msg, activity, task, userTaskRecordDO, LocalDate.now(), commissionInfoDTO);

            //任务进度更新
            TIrisActivityAwardRecordDO awardRecordDO = null;
            if (BooleanUtils.isTrue(UserTaskStatusEnum.FINISHED.getCode() == userTaskRecordDO.getUserTaskStatus())) {
                eventHandingInfoList.add("邀请有礼任务达成");
                log.info("邀请有礼任务达成,uid:{},commissionInfoDTO:{},msg:{}", msg.getUid(),
                        JSON.toJSONString(commissionInfoDTO), JSON.toJSONString(msg));
                //发放权益
                awardRecordDO = activityCommonService.sendAwardForSupervisor(userTaskRecordDO, task);
            } else if (UserTaskStatusEnum.LIMITED.getCode() == userTaskRecordDO.getUserTaskStatus()) {
                eventHandingInfoList.add("邀请有礼任务超上限");
            } else {
                eventHandingInfoList.add("邀请有礼任务未达成");
            }

            //更新用户活动任务完成进度
            userTaskRecordService.updateUserTaskRecordStatus(userTaskRecordDO, awardRecordDO);
        } finally {
            if (lock != null) {
                lock.unlock();
            }
        }
    }

    @Override
    public void process(ActivityEventDTO msg, ActivityDTO activity, ActivityTaskDTO task,
            List<String> eventHandingInfoList, TaskExecutorExtendInfo taskExecutorExtendInfo) {
        String uid = msg.getUid();
        //查询成为见习时间
        Long becomeTraineeTime = activityCommonService.queryTraineeTime(msg.getUid());
        if (becomeTraineeTime < activityCommonService.queryInviteUpperLimitChangeTime(uid)){
            log.info("IrisInviteBecomeTraineeTaskExecutor processOld uid:{},msgId:{}",uid,msg.getIdempotent());
            processOld(msg,activity,task,eventHandingInfoList);
        } else {
            log.info("IrisInviteBecomeTraineeTaskExecutor processNew uid:{},msgId:{}",uid,msg.getIdempotent());
            irisInviteNewTaskExecutor.process(msg,activity,task,eventHandingInfoList,taskExecutorExtendInfo);
        }
    }

    public CommissionInfoDTO getCommissionInfo(String uid, Date becomeTraineeTime, Date taskEndTime) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String startTime = sdf.format(becomeTraineeTime);
        String endTime = sdf.format(taskEndTime);

        return activityCommonService.queryInviteTaskTarget(uid, startTime, endTime, false);
    }

    private InviteLimitDTO queryInviteLimit(String pid, Long becomeTraineeTime) {
        InviteLimitDTO inviteLimitDTO = activityCommonService.queryInviteLimit(pid, becomeTraineeTime);
        if (inviteLimitDTO.isError()) {
            log.error("查询邀请上限失败，pid：{}，inviteLimitDTO：{}", pid, JSON.toJSONString(inviteLimitDTO));
            throw new TRetailErrorException(inviteLimitDTO.getDesc() + "，查询邀请上限失败");
        }
        return inviteLimitDTO;
    }

    private void addParameterForTask(ActivityTaskDTO task, ActivityEventDTO msg) {
        task.parseLimitCondition();
    }

    public void fillTaskRecordStatus(ActivityEventDTO eventDTO, ActivityDTO act, ActivityTaskDTO task,
            TIrisUserTaskRecordDO userTaskRecordDO, LocalDate currentLocalDate, CommissionInfoDTO commissionInfoDTO) {
        if (UserTaskStatusEnum.LIMITED.getCode() == userTaskRecordDO.getUserTaskStatus()) {
            log.info("该用户处于超上限任务状态,uid={},pid={}, msg={}", eventDTO.getUid(), eventDTO.getPid(),
                    JSONObject.toJSONString(eventDTO));
            return;
        }
        LocalDate currentTaskStartDate = userTaskRecordDO.getCurrentTaskStartTime().toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
        LocalDate currentTaskEndDate = userTaskRecordDO.getCurrentTaskEndTime().toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();

        int plusDays = activityConfig.getCommissionPlusDays(userTaskRecordDO.getUid());
        LocalDate currentTaskEndDatePlus60 = userTaskRecordDO.getCurrentTaskEndTime().toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate().plusDays(plusDays);

        Integer gmv = userTaskRecordDO.getGmv();
        Integer waitCommissionGmv = commissionInfoDTO.getWaitCommissionGmv();
        boolean commissionSuccess = false;
        if (eventDTO.getEventType().equals(ActivityEventTypeEnum.DISTRIBUTION_ORDER.getCode())) {
            commissionSuccess = dealDistributionOrderEvent(eventDTO, currentTaskStartDate, currentTaskEndDate);
        }
        //在任务周期内
        if (LocalDateUtil.isLocalDateBetween(currentLocalDate, currentTaskStartDate, currentTaskEndDate)) {
            //设置有效gmv任务完成状态
            if (gmv > task.getGmv()) {
                userTaskRecordDO.setEstimateUserTaskStatus(UserTaskStatusEnum.FINISHED.getCode());
            } else {
                userTaskRecordDO.setEstimateUserTaskStatus(UserTaskStatusEnum.PROGRESS.getCode());
            }
            //实际佣金条件判断
            if (commissionSuccess) {
                //任务已完成
                userTaskRecordDO.setUserTaskStatus(UserTaskStatusEnum.FINISHED.getCode());
                userTaskRecordDO.setEstimateUserTaskStatus(UserTaskStatusEnum.FINISHED.getCode());
            } else {
                userTaskRecordDO.setUserTaskStatus(UserTaskStatusEnum.PROGRESS.getCode());
            }
            //在任务周期结束后60天内
        } else if (currentLocalDate.isAfter(currentTaskEndDate)
                && (currentLocalDate.equals(currentTaskEndDatePlus60) || currentLocalDate
                .isBefore(currentTaskEndDatePlus60))) {
            //设置有效gmv任务完成状态
            LocalDate endDatePlusOne = currentTaskEndDate.plusDays(1);
            if (gmv > task.getGmv()) {
                userTaskRecordDO.setEstimateUserTaskStatus(UserTaskStatusEnum.FINISHED.getCode());
            } else if (currentLocalDate.isAfter(endDatePlusOne)) {
                //有效gmv没达成，且已过任务结束时间24小时，直接判断为未完成
                userTaskRecordDO.setEstimateUserTaskStatus(UserTaskStatusEnum.UNFINISHED.getCode());
                userTaskRecordDO.setUserTaskStatus(UserTaskStatusEnum.UNFINISHED.getCode());
                return;
            }
            //实际佣金条件判断
            if (commissionSuccess) {
                //任务已完成
                userTaskRecordDO.setUserTaskStatus(UserTaskStatusEnum.FINISHED.getCode());
                userTaskRecordDO.setEstimateUserTaskStatus(UserTaskStatusEnum.FINISHED.getCode());
            } else {
                userTaskRecordDO.setUserTaskStatus(UserTaskStatusEnum.PROGRESS.getCode());
            }
            //任务周期结束60天后
        } else if (currentLocalDate.isAfter(currentTaskEndDatePlus60)) {
            //实际佣金及分享次数条件判断
            if (waitCommissionGmv > task.getGmv()) {
                //任务已完成
                userTaskRecordDO.setUserTaskStatus(UserTaskStatusEnum.FINISHED.getCode());
                userTaskRecordDO.setEstimateUserTaskStatus(UserTaskStatusEnum.FINISHED.getCode());
            } else {
                userTaskRecordDO.setUserTaskStatus(UserTaskStatusEnum.UNFINISHED.getCode());
                userTaskRecordDO.setEstimateUserTaskStatus(UserTaskStatusEnum.UNFINISHED.getCode());
            }
        }
    }

    private boolean dealDistributionOrderEvent(ActivityEventDTO eventDTO, LocalDate currentTaskStartDate
            , LocalDate currentTaskEndDate) {
        boolean result = false;
        String eventInfo = eventDTO.getEventInfo();
        DistributionOrderEvent event = JsonUtils.parse(eventInfo,
                DistributionOrderEvent.class);
        //查询是否走mock分销单数据
        DistributionOrderEvent mockDTO = responseMockConfig.getMockDistributionOrderEvent(eventDTO.getUid());
        if (mockDTO != null) {
            log.info("dealDistributionOrderEvent命中mock数据白名单，直接返回mock数据，uid:{}，mockDTO:{},eventDTO:{}"
                    , eventDTO.getUid(), JSON.toJSONString(mockDTO), JSON.toJSONString(eventDTO));
            event = mockDTO;
        }
        String commissionStatus = event.getCommissionStatus();
        String orderStatus = event.getOrderStatus();
        Long gmv = event.getGmv();
        Long orderCreateTime = event.getOrderCreateTime();
        Long finishTime = event.getFinishTime();

        if (orderStatus.equals(OrderStatusEnum.SUCCESS.getCode()) &&
                Objects.nonNull(finishTime) && gmv > 0) {
            LocalDate localDate = new Date(orderCreateTime).toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDate();
            if (LocalDateUtil.isLocalDateBetween(localDate, currentTaskStartDate, currentTaskEndDate)) {
                result = true;
            }
        }
        log.info("分佣单消息处理结果,commissionStatus:{},orderStatus:{},gmv:{},finishTime:{},result:{},uid:{}",
                commissionStatus, orderStatus, gmv, finishTime, result, eventDTO.getUid());
        return result;
    }

    @Override
    public void fillTaskRecordStatus(ActivityEventDTO eventDTO, ActivityDTO act, ActivityTaskDTO task,
            TIrisUserTaskRecordDO userTaskRecordDO, LocalDate currentLocalDate) {

    }

    @Override
    public boolean executeAward(ActivityEventDTO event, ActivityDTO act, ActivityTaskDTO task) {
        return false;
    }

    @Override
    public boolean checkTaskThreshold(ActivityEventDTO event, ActivityDTO act, ActivityTaskDTO task,
            BaseUserTaskDataVO userTaskDataVO) {
        return false;
    }

    @Override
    public BaseUserTaskDataVO prepareUserTaskData(ActivityEventDTO event, ActivityDTO act, ActivityTaskDTO task) {
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    public TIrisUserTaskRecordDO buildUserTaskRecordDO(ActivityDTO activity, ActivityTaskDTO taskDTO,
            ActivityEventDTO msg, Long becomeTraineeTime, InviteLimitDTO inviteLimitDTO) {
        TIrisUserTaskRecordDO userTaskRecordDO = new TIrisUserTaskRecordDO();
        userTaskRecordDO.setBizId(idCreateorUtil.genId());
        userTaskRecordDO.setUid(msg.getUid());
        userTaskRecordDO.setPid(msg.getPid());
        userTaskRecordDO.setActivityId(activity.getBizId());
        userTaskRecordDO.setTaskId(taskDTO.getBizId());
        userTaskRecordDO.setActivityType(activity.getActivityType());
        userTaskRecordDO.setPeriodType(activity.getPeriodType());
        userTaskRecordDO.setUserTaskStatus(UserTaskStatusEnum.PROGRESS.getCode());
        userTaskRecordDO.setEstimateUserTaskStatus(UserTaskStatusEnum.PROGRESS.getCode());
        //任务结束时间
        //Date taskEndTime = getTaskEndTime(taskDTO, becomeTraineeTime);
        userTaskRecordDO.setCurrentTaskStartTime(new Date(becomeTraineeTime));
        userTaskRecordDO.setCurrentTaskEndTime(TraineeTimeTo29(becomeTraineeTime));
        userTaskRecordDO.setTier(taskDTO.getTier());
        //根据上限和已邀请人数设置任务状态和次序
        setLimitInfoRecord(msg, inviteLimitDTO, userTaskRecordDO);
        userTaskRecordService.saveUserTaskRecord(userTaskRecordDO);
        //邀请上限缓存递增
        userTaskRecordService.incrInviteLimitCache(msg.getPid(), msg.getUid(), userTaskRecordDO.getIsParticipated());
        return userTaskRecordDO;
    }

    private void setLimitInfoRecord(ActivityEventDTO msg, InviteLimitDTO inviteLimitDTO,
            TIrisUserTaskRecordDO userTaskRecordDO) {
        //如果超出上限，则返回LIMITED
        userTaskRecordDO.setIsParticipated(TaskBatchDateIsParticipatedEnum.YES.getCode());
        if (BooleanUtils.isFalse(inviteLimitDTO.isSatisfy())) {
            userTaskRecordDO.setIsParticipated(TaskBatchDateIsParticipatedEnum.LIMITED.getCode());
            userTaskRecordDO.setUserTaskStatus(UserTaskStatusEnum.LIMITED.getCode());
            userTaskRecordDO.setEstimateUserTaskStatus(UserTaskStatusEnum.LIMITED.getCode());
        }
        userTaskRecordDO.setInviteLimit(inviteLimitDTO.getInviteLimit());
        userTaskRecordDO.setInviteSeq(inviteLimitDTO.getInviteSeq());
    }

}
